﻿using System;
using System.Collections.Generic;
using System.Configuration;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using EP.Base.BaseService.Manager.Config;
using EP.Base.BaseService.Manager.Interface.Common;
using EP.Base.DataTool.DataRedis;
using Newtonsoft.Json;

namespace EP.Base.BaseService.Manager.Common
{
    /// <summary>
    /// 服务监控器
    /// </summary>
    public class ServiceMonitor:IServiceMonitor
    {
        /// <summary>
        /// 当前服务运行状态
        /// </summary>
        private bool _currentServiceState;
        /// <summary>
        /// 当前服务编号
        /// </summary>
        private readonly int _currentServiceNum;

        /// <summary>
        /// redis空间
        /// </summary>
        private readonly string _keySpace;
        /// <summary>
        /// 当前运行的服务存储key
        /// </summary>
        private readonly string _serviceMonitorCurrentNameRedisKey;
        /// <summary>
        ///用于热切换主运行服务的服务存储key
        /// </summary>
        private readonly string _serviceMonitorReplaceNameRedisKey;
        /// <summary>
        /// 当前所有服务的列表存储key
        /// </summary>
        private readonly string _serviceMonitorServiceListRedisKey;
        /// <summary>
        /// 服务监控间隔 秒
        /// </summary>
        private readonly int _monitorInterval;
        private readonly RedisHelper _redisHelper;
        private readonly Task _monitorTask;
        private readonly Task _heartbeatTask;

        /// <summary>
        /// 待执行的任务
        /// </summary>
        private IAutoMonitorService _job;
        
        //任务控制变量  
        private static readonly CancellationTokenSource Cts = new CancellationTokenSource();

        /// <summary>
        /// 状态判断容差 秒
        /// </summary>
        private readonly int _toleranceSeconds = 3;

        /// <summary>
        /// 服务名称
        /// </summary>
        private readonly string _serviceName;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="monitorInterval">服务监控间隔 秒</param>
        /// <param name="currentServiceNum">当前服务编号</param>
        /// <param name="serviceMonitorRedisConnectionString">redis</param>
        /// <param name="keySpace">redis空间</param>
        /// <param name="serviceMonitorCurrentNameRedisKey">当前运行的服务存储key</param>
        /// <param name="serviceMonitorServiceListRedisKey">当前所有服务的列表存储key</param>
        /// <param name="serviceMonitorReplaceNameRedisKey">用于热切换主运行服务的服务存储key</param>
        /// <param name="serviceName">服务名称</param>
        /// <param name="toleranceSeconds">状态判断容差 秒</param>
        public ServiceMonitor(int monitorInterval, 
            int currentServiceNum, 
            string serviceMonitorRedisConnectionString, 
            string keySpace, 
            string serviceMonitorCurrentNameRedisKey,
            string serviceMonitorServiceListRedisKey, string serviceMonitorReplaceNameRedisKey, string serviceName="", int toleranceSeconds = 3)
        {
            _monitorInterval = monitorInterval;
            _currentServiceNum = currentServiceNum;
            _serviceMonitorCurrentNameRedisKey = serviceMonitorCurrentNameRedisKey;
            _serviceMonitorServiceListRedisKey = serviceMonitorServiceListRedisKey;
            _keySpace = keySpace;
            _toleranceSeconds = toleranceSeconds;
            _serviceName = serviceName;
            _serviceMonitorReplaceNameRedisKey = serviceMonitorReplaceNameRedisKey;
            _currentServiceState = false;
            _monitorTask = new Task(Monitor);
            _heartbeatTask = new Task(Heartbeat);
            _redisHelper = RedisHelper.GetRedisHelper(serviceMonitorRedisConnectionString);
        }
        /// <summary>
        /// 
        /// </summary>
        public ServiceMonitor()
        {
            var config= (ServiceMonitorConfigSection)ConfigurationManager.GetSection(ServiceMonitorConfigSection.SectionName);
            if(config==null)
                throw new ArgumentNullException("ServiceMonitor-config" + "未配置！");
            _monitorInterval = config.MonitorInterval;
            _currentServiceNum = config.CurrentServiceNum;
            _serviceMonitorCurrentNameRedisKey = config.ServiceMonitorCurrentNameRedisKey;
            _serviceMonitorServiceListRedisKey = config.ServiceMonitorServiceListRedisKey;
            _keySpace = config.KeySpace;
            _toleranceSeconds = config.ToleranceSeconds;
            _serviceName = config.ServiceName;
            _serviceMonitorReplaceNameRedisKey = config.ServiceMonitorReplaceNameRedisKey;
            _currentServiceState = false;
            _monitorTask = new Task(Monitor);
            _heartbeatTask = new Task(Heartbeat);
            _redisHelper = RedisHelper.GetRedisHelper(config.ServiceMonitorRedisConnectionString);
        }

        /// <summary>
        /// 心跳
        /// </summary>
        private void Heartbeat()
        {
            while (!Cts.IsCancellationRequested)
            {
                try
                {
                    //刷新自己
                    InsertServices();
                }
                catch (Exception ex)
                {
                    WriteErrorMsg("服务监控在发送心跳包时异常，异常信息："+ex.Message+" - "+ex.StackTrace +" - "+ex.Source);
                }
                //间隔
                Thread.Sleep(_monitorInterval * 1000);
            }
        }

        #region  Write messges
        /// <summary>
        /// 输出异常
        /// </summary>
        /// <param name="msg"></param>
        private void WriteErrorMsg(string msg)
        {
            if (_job!=null)
                _job.WriteErrorMsg("监控服务ERROR消息：服务" + _serviceName + "信息：" + msg);
        }
        /// <summary>
        /// 输出异常
        /// </summary>
        /// <param name="msg"></param>
        private void WriteDebugMsg(string msg)
        {
            if (_job != null)
                _job.WriteDebugMsg("监控服务DEBUG消息：服务" + _serviceName + "信息：" + msg);
        }
        #endregion

        /// <summary>
        /// 检测
        /// </summary>
        private void Monitor()
        {
            while (!Cts.IsCancellationRequested)
            {
                try
                {

                    #region 监控
                    //检测当前运行服务是否还健在？
                    if (!CheckRuningServiceState())
                    {
                        //选举新服务
                        var newService = new ServiceInfo { Num = XuanJu(),Time = DateTime.Now};
                        WriteDebugMsg("切换运行服务为 service=" + ObjToJson(newService));
                        SetCurrentService(newService);
                    
                    }
                     var service = GetCurrentRuningService();

                     WriteDebugMsg(string.Format("当前服务Num={0}，当前服务状态_currentServiceState={1}，主服务Num={2}", _currentServiceNum,_currentServiceState, service.Num));

                    //检测主服务是否选举为自己
                    if (service.Num == _currentServiceNum && !_currentServiceState)
                    {
                        WriteDebugMsg("提升服务为主服务，开始运行业务！ _currentServiceNum=" + _currentServiceNum);
                        Run();
                    }
                    else if (service.Num != _currentServiceNum && _currentServiceState)//检测是否 切换了机器
                    {
                        WriteDebugMsg(string.Format("主服务已切换到{0}，停止执行当前服务_currentServiceNum={1}！", service.Num, _currentServiceNum));
                        _job.Stop();
                        _currentServiceState = false;
                    }
                    else if (service.Num == _currentServiceNum && _currentServiceState)//如果是正在运行 那就检测有没有热切换服务
                    {
                        int num = 0;
                        if (_redisHelper.IsExistsKey(_keySpace, _serviceMonitorReplaceNameRedisKey))
                        {
                            num=_redisHelper.StringGet<int>(_keySpace, _serviceMonitorReplaceNameRedisKey);
                        }
                        if (CheckServiceState(num))
                        {
                            WriteDebugMsg(string.Format("发现需要切主服务到服务{0}，当前服务_currentServiceNum={1}！", num, _currentServiceNum));
                            if (num != _currentServiceNum)
                            {
                                //不是自己就先把自己停了
                                _job.Stop();
                                _currentServiceState = false;
                            }
                            //切到主服务
                            var newService = new ServiceInfo { Num = num, Time = DateTime.Now };
                            SetCurrentService(newService);
                            _redisHelper.DelKey(_keySpace, _serviceMonitorReplaceNameRedisKey);
                        }
                    }
                    var services = GetAllServiceList();
                    if (services == null)
                    {
                        WriteErrorMsg(_serviceName + " - " + _keySpace +" - "+ "发现服务列表中无服务！");
                    }
                    else if (services.Where(CheckServiceState).Count() < services.Count)
                    {
                        string errorservice =ObjToJson(services.Where(n => !CheckServiceState(n)).Select(n => n.Num).ToList());
                        WriteErrorMsg(_serviceName + " - " + _keySpace + " - " + "发现有服务异常！服务列表：" + errorservice);
                    }
                #endregion

                }
                catch (Exception ex)
                {
                   WriteErrorMsg("服务监控在定时监控时异常，异常信息：" + ex.Message + " - " + ex.StackTrace + " - " + ex.Source);
                }
                //间隔
                Thread.Sleep(_monitorInterval*1000);
            }
        }

        #region service redis data handel
        private bool CheckRuningServiceState()
        {
            if (!_redisHelper.IsExistsKey(_keySpace, _serviceMonitorCurrentNameRedisKey))
                return false;
            int runing = GetCurrentRuningService().Num;
            return CheckServiceState(runing);
        }

        private ServiceInfo GetCurrentRuningService()
        {
            return _redisHelper.StringGet<ServiceInfo>(_keySpace, _serviceMonitorCurrentNameRedisKey);
        }

        private bool CheckServiceState(int num)
        {
             var services = GetAllServiceList();
            if (services == null || services.Count == 0)
                return false;
            var service = services.FirstOrDefault(n => n.Num == num);
            if (service == null)
                return false;
            return CheckServiceState(service);

        }
        private bool CheckServiceState(ServiceInfo service)
        {
            if (service == null)
                return false;
            return service.Time > DateTime.Now.AddSeconds(-(_monitorInterval + _toleranceSeconds));

        }
        private int XuanJu()
        {
            var services = GetAllServiceList();

            if (services == null || services.Count == 0)
                return _currentServiceNum;
            //选举一个最新的服务编号
            return services.Where(CheckServiceState).Min(n => n.Num);
        }

        private List<ServiceInfo> GetAllServiceList()
        {
            var services = new List<ServiceInfo>();

            var servicesResult = _redisHelper.HashGet<ServiceInfo>(_keySpace, _serviceMonitorServiceListRedisKey);
            if (servicesResult != null)
            {
                services.AddRange(servicesResult.Select(n => n.Value));
            }
            return services;
        }

        private void SetCurrentService(ServiceInfo service)
        {
            _redisHelper.StringSet(_keySpace, _serviceMonitorCurrentNameRedisKey, service);
            WriteDebugMsg("设置主服务 service=" + ObjToJson(service));
        }
        /// <summary>
        /// 写入/刷新自己
        /// </summary>
        private void InsertServices()
        {
            var service = new ServiceInfo
            {
                Num = _currentServiceNum,
                Time = DateTime.Now
            };
            _redisHelper.HashSet(_keySpace, _serviceMonitorServiceListRedisKey,_currentServiceNum.ToString(CultureInfo.InvariantCulture), service);

        }
        #endregion
        private void Run()
        {
            if (!_currentServiceState)
            {
                _currentServiceState = true;
                if (_job != null)
                    _job.Start();
            }
        }

        private string ObjToJson(object obj)
        {
            if (obj == null)
                return string.Empty;
            return JsonConvert.SerializeObject(obj);
        }

        #region public api
        /// <summary>
        /// 注册一个要等待运行对象(当监控发现其它服务牺牲后会执行)
        /// </summary>
        public void Register<T>(T job) where T : IAutoMonitorService
        {
            _job = job;
            WriteDebugMsg("注册一个等待调用的服务 IAutoMonitorService!");
        }

        public void Start()
        {
            if(_heartbeatTask!=null)
                _heartbeatTask.Start();
            Thread.Sleep(1000);
            if(_monitorTask!=null)
                _monitorTask.Start();
        }

        /// <summary>
        /// 停止服务监控
        /// </summary>
        public void Stop()
        {
            Cts.Cancel();
            WriteDebugMsg("停止服务监控!");
            _job.Stop();
            Thread.Sleep(1000);
            _job.Dispose();
            _currentServiceState = false;
        }
        #endregion
    }

    public class ServiceInfo
    {
        public int Num { get; set; }

        public DateTime Time { get; set; }
    }
    
}
